import Mock from "mockjs";
import qs from "querystring"

Mock.mock("/api/blogtype", "get", {
  code: 0,
  msg: "",
  "data|10-20": [
    {
      "id|+1": 1,
      name: "分类@id",
      "articleCount|0-300": 0,
      "order|+1": 1,
    },
  ],
});

Mock.mock(/^\/api\/blog(\?.+)?$/, "get", function (options) {
  const query = qs.parse(options.url);

  return Mock.mock({
    code: 0,
    msg: "",
    data: {
      "total|2000-3000": 0,
      [`rows|${query.limit || 10}`]: [
        {
          id: "@guid",
          title: "@ctitle(1, 50)",
          description: "@cparagraph(1, 10)",
          category: {
            "id|1-10": 0,
            name: "分类@id",
          },
          "scanNumber|0-3000": 0,
          "commentNumber|0-300": 30,
          "thumb|1": [
            Mock.Random.image("300x250", "#000", "#fff", "Random Image"),
            null,
          ],
          createDate: `@date('T')`,
        },
      ],
    },
  });
});

Mock.mock(/^\/api\/blog\/[^/]+$/, "get", {
  code: 0,
  msg: "",
  data: {
    id: "@guid",
    title: "fastify-网关服务器",
    category: {
      "id|1-10": 1,
      name: "分类@id",
    },
    description: "@cparagraph(1, 10)",
    "scanNumber|0-10000": 0,
    "commentNumber|0-100": 0,
    createDate: "@date('T')",
    toc: [
      {
        name: "插件",
        anchor: "article-md-title-5",
      },
      {
        name: "代理配置",
        anchor: "article-md-title-6",
      },
      {
        name: "其他配置",
        anchor: "article-md-title-7",
      },
      {
        name: "示例",
        anchor: "article-md-title-7",
      },
    ],
    "thumb|1": [
      Mock.Random.image("300x250", "#000", "#fff", "Random Image"),
      null,
    ],
    htmlContent: `<p class="has-line-data" data-line-start="0" data-line-end="1">网关服务器 主要实现了传统web后端的解耦，聚合多个微服务，实现路由管理、限流、熔断、负载均衡、熔断等操作</p>
<h2 class="code-line" data-line-start=4 data-line-end=5 ><a id="_4"></a>插件</h2>
<pre><code class="has-line-data" data-line-start="7" data-line-end="13" class="language-js"><span class="hljs-keyword">import</span> fastify <span class="hljs-keyword">from</span> <span class="hljs-string">'fastify'</span>
<span class="hljs-keyword">import</span> proxy <span class="hljs-keyword">from</span> <span class="hljs-string">'@fastify/http-proxy'</span> //负载代理技术
<span class="hljs-keyword">import</span> rateLimit <span class="hljs-keyword">from</span> <span class="hljs-string">'@fastify/rate-limit'</span> //限流技术
<span class="hljs-keyword">import</span> caching <span class="hljs-keyword">from</span> <span class="hljs-string">'@fastify/caching'</span> //缓存技术
<span class="hljs-keyword">import</span> CircuitBreaker <span class="hljs-keyword">from</span> <span class="hljs-string">'opossum'</span> //熔断技术
</code></pre>
<h3 class="code-line" data-line-start=14 data-line-end=15 ><a id="_14"></a>代理配置</h3>
<pre><code class="has-line-data" data-line-start="16" data-line-end="31" class="language-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> [
  {
    upstream: <span class="hljs-string">'http://localhost:9001'</span>, <span class="hljs-comment">//代理地址</span>
    prefix: <span class="hljs-string">'/pc'</span>, <span class="hljs-comment">//前缀</span>
    rewritePrefix: <span class="hljs-string">''</span>, <span class="hljs-comment">//实际请求将pc 替换成 '' 因为后端服务器没有pc这个路由</span>
    httpMethods: [<span class="hljs-string">'GET'</span>, <span class="hljs-string">'POST'</span>], <span class="hljs-comment">//允许的请求方式</span>
  },
  {
    upstream: <span class="hljs-string">'http://localhost:9002'</span>,
    prefix: <span class="hljs-string">'/mobile'</span>,
    rewritePrefix: <span class="hljs-string">''</span>,
    httpMethods: [<span class="hljs-string">'GET'</span>, <span class="hljs-string">'POST'</span>],
  }
]
</code></pre>
<h3 class="code-line" data-line-start=32 data-line-end=33 ><a id="_32"></a>其他配置</h3>
<pre><code class="has-line-data" data-line-start="34" data-line-end="50" class="language-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> rateLimitConfig = {
  max: <span class="hljs-number">5</span>, <span class="hljs-comment">//每 1 分钟最多允许 5 次请求</span>
  timeWindow: <span class="hljs-string">'1 minute'</span>, <span class="hljs-comment">//一分钟</span>
} 

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> cachingConfig = {
  privacy: <span class="hljs-string">'private'</span>, <span class="hljs-comment">//缓存客户端服务器 禁止缓存代理服务器</span>
  expiresIn: <span class="hljs-number">1000</span> <span class="hljs-comment">//缓存1s</span>
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> breakerConfig = {
  errorThresholdPercentage: <span class="hljs-number">40</span>, <span class="hljs-comment">//超过 40% 会触发熔断</span>
  timeout: <span class="hljs-number">1000</span>, <span class="hljs-comment">//超过 1s 会触发熔断</span>
  resetTimeout: <span class="hljs-number">5000</span>, <span class="hljs-comment">//熔断后 5s 会重置</span>
}
</code></pre>
<h3 class="code-line" data-line-start=50 data-line-end=51 ><a id="_50"></a>示例</h3>
<pre><code class="has-line-data" data-line-start="52" data-line-end="93" class="language-js"><span class="hljs-keyword">import</span> proxyConfig <span class="hljs-keyword">from</span> <span class="hljs-string">'./proxy/index.js'</span> //代理配置
// 其他配置
<span class="hljs-keyword">import</span> { rateLimitConfig, cachingConfig, breakerConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">'./config/index.js'</span>

const app = fastify({
  logger: false
})

//熔断技术
const breaker = new CircuitBreaker((url) =&gt; {
  return fetch(url).then((res) =&gt; res.json()) //检测服务是否挂掉
}, breakerConfig)


app.register(caching, cachingConfig) //注册缓存服务

app.register(rateLimit, rateLimitConfig) //注册限流

  

// 配置代理
proxyConfig.forEach(({ upstream, prefix, rewritePrefix, httpMethods }) =&gt; {
  app.register(proxy, {
    //请求代理服务之前触发熔断
    preHandler: (request, reply, done) =&gt; {
      //检测这个服务 如果服务挂掉立马熔断
      breaker.fire(upstream).then(() =&gt; done()).catch(() =&gt; reply.code(503).send(<span class="hljs-string">'Circuit breaker tripped'</span>))
    },
    upstream,
    prefix,
    rewritePrefix,
    httpMethods
  }
})

  
  

//启动服网关
app.listen({ port: 3000 }).then(() =&gt; console.log(<span class="hljs-string">'server running on port 3000'</span>))
</code></pre>
<hr>
<p class="has-line-data" data-line-start="98" data-line-end="99">使用的包：</p>
<h3 class="code-line" data-line-start=99 data-line-end=100 ><a id="fastifyhttpproxy__99"></a>@fastify/http-proxy 包</h3>
<p class="has-line-data" data-line-start="100" data-line-end="101">[[@fastify http-proxy]]</p>
<ul>
<li class="has-line-data" data-line-start="101" data-line-end="103">
<p class="has-line-data" data-line-start="101" data-line-end="102"><strong>使用场景</strong> ：当需要将请求代理到其他服务器时使用，比如在微服务架构中，网关服务器需要将不同路由的请求转发到不同的后端服务。</p>
</li>
<li class="has-line-data" data-line-start="103" data-line-end="105">
<p class="has-line-data" data-line-start="103" data-line-end="104"><strong>用途</strong> ：实现请求的转发和代理功能，使得网关服务器可以根据配置的规则，将客户端的请求转发给相应的后端服务进行处理，从而隐藏后端服务的具体地址和实现细节，提供统一的入口。</p>
</li>
<li class="has-line-data" data-line-start="105" data-line-end="107">
<p class="has-line-data" data-line-start="105" data-line-end="106"><strong>底层原理</strong> ：当接收到客户端请求后，根据配置的 upstream（后端服务地址）、prefix（请求路径前缀）等规则，将请求重新构造并发送到目标服务器。在转发过程中，可以对请求进行一些处理，如修改请求头、请求体等。后端服务返回的响应也会被代理服务器接收并转发回客户端。</p>
</li>
<li class="has-line-data" data-line-start="107" data-line-end="108">
<p class="has-line-data" data-line-start="107" data-line-end="108"><strong>在网关服务器中的作用</strong> ：作为核心组件，实现了网关的代理功能，使得网关能够将不同类型、不同业务的请求分发到对应的后端服务，实现服务的解耦和统一管理。</p>
</li>
</ul>
<h3 class="code-line" data-line-start=110 data-line-end=111 ><a id="fastifyratelimit__110"></a>@fastify/rate-limit 包</h3>
<p class="has-line-data" data-line-start="111" data-line-end="112">[[@fastify rate-limit]]</p>
<ul>
<li class="has-line-data" data-line-start="112" data-line-end="114">
<p class="has-line-data" data-line-start="112" data-line-end="113"><strong>使用场景</strong> ：在需要限制客户端请求频率的场景中使用，例如防止某些客户端对服务器进行恶意的高频次请求，或者对免费用户的请求频率进行限制以保证服务的公平性和稳定性。</p>
</li>
<li class="has-line-data" data-line-start="114" data-line-end="116">
<p class="has-line-data" data-line-start="114" data-line-end="115"><strong>用途</strong> ：对客户端的请求进行限流，控制在一定时间窗口内每个客户端最多可以发送的请求数量，从而保护服务器免受过多请求的冲击。</p>
</li>
<li class="has-line-data" data-line-start="116" data-line-end="118">
<p class="has-line-data" data-line-start="116" data-line-end="117"><strong>底层原理</strong> ：通常采用令牌桶算法或漏桶算法来实现。对于每个客户端，维护一个计数器和时间窗口，每当客户端发送一个请求，计数器加 1，如果计数器超过设定的最大值，则拒绝该请求，直到时间窗口过去后计数器重置。</p>
</li>
<li class="has-line-data" data-line-start="118" data-line-end="119">
<p class="has-line-data" data-line-start="118" data-line-end="119"><strong>在网关服务器中的作用</strong> ：通过限制客户端的请求频率，保护后端服务的稳定运行，避免因过多请求导致服务器过载甚至崩溃，同时也有助于实现对不同客户端的差异化服务策略。</p>
</li>
</ul>
<h3 class="code-line" data-line-start=121 data-line-end=122 ><a id="fastifycaching__121"></a>@fastify/caching 包</h3>
<p class="has-line-data" data-line-start="122" data-line-end="123">[[@fastify caching]]</p>
<ul>
<li class="has-line-data" data-line-start="123" data-line-end="125">
<p class="has-line-data" data-line-start="123" data-line-end="124"><strong>使用场景</strong> ：当需要对响应内容进行缓存，以提高性能和减轻后端服务负载时使用，例如对于一些频繁访问且数据变化不频繁的资源，如静态页面、公共数据等。</p>
</li>
<li class="has-line-data" data-line-start="125" data-line-end="127">
<p class="has-line-data" data-line-start="125" data-line-end="126"><strong>用途</strong> ：对后端服务返回的响应进行缓存，在后续相同请求到来时，直接从缓存中返回数据，减少对后端服务的请求次数，提高响应速度。</p>
</li>
<li class="has-line-data" data-line-start="127" data-line-end="129">
<p class="has-line-data" data-line-start="127" data-line-end="128"><strong>底层原理</strong> ：根据配置的缓存策略，如隐私级别（private 表示缓存仅对客户端和服务器有效，不允许缓存代理服务器缓存）、过期时间（expiresIn）等，将响应数据存储在内存或指定的缓存存储中。当收到后续请求时，先检查缓存中是否存在有效数据，如果存在则直接返回缓存数据，否则向后端服务发送请求并更新缓存。</p>
</li>
<li class="has-line-data" data-line-start="129" data-line-end="130">
<p class="has-line-data" data-line-start="129" data-line-end="130"><strong>在网关服务器中的作用</strong> ：通过缓存机制，减少对后端服务的依赖，提高整个系统的性能和响应速度，同时也降低了服务器的负载，尤其对于高并发场景下的性能提升效果明显。</p>
</li>
</ul>
<h3 class="code-line" data-line-start=132 data-line-end=133 ><a id="opossum__132"></a>opossum 包（用于熔断技术）</h3>
<p class="has-line-data" data-line-start="133" data-line-end="134">[[opossum]]</p>
<ul>
<li class="has-line-data" data-line-start="134" data-line-end="136">
<p class="has-line-data" data-line-start="134" data-line-end="135"><strong>使用场景</strong> ：在调用外部服务或后端服务可能存在不稳定或故障的情况下使用，例如当某个后端服务偶尔会出现超时、错误率较高等问题时。</p>
</li>
<li class="has-line-data" data-line-start="136" data-line-end="138">
<p class="has-line-data" data-line-start="136" data-line-end="137"><strong>用途</strong> ：实现熔断器模式，当检测到服务出现故障（如错误率超过设定阈值、响应超时等）时，快速熔断对故障服务的请求，避免故障扩散，保护整个系统的稳定运行。</p>
</li>
<li class="has-line-data" data-line-start="138" data-line-end="140">
<p class="has-line-data" data-line-start="138" data-line-end="139"><strong>底层原理</strong> ：维护一个状态机，初始状态为关闭状态，正常转发请求。当故障次数达到一定阈值（如错误率超过 errorThresholdPercentage）时，熔断器切换到打开状态，此时直接拒绝后续请求，并返回错误信息。经过一段时间（resetTimeout）后，熔断器进入半开状态，尝试放行部分请求，如果请求成功则恢复到关闭状态，否则继续维持打开状态。</p>
</li>
<li class="has-line-data" data-line-start="140" data-line-end="141">
<p class="has-line-data" data-line-start="140" data-line-end="141"><strong>在网关服务器中的作用</strong> ：能够快速感知后端服务的故障并做出响应，避免因故障服务拖垮整个系统，保障系统的高可用性和稳定性，同时也为故障恢复提供了缓冲时间。</p>
</li>
</ul>
`,
  },
});


Mock.mock("/api/comment", "post", {
  code: 0,
  msg: "",
  data: {
    id: "@guid",
    nickname: "@cname",
    content: "@cparagraph(1, 10)",
    createDate: Date.now(),
    "avatar|1": [
      "https://qiheizhiya.oss-cn-shenzhen.aliyuncs.com/image/avatar6.jpg",
      "https://qiheizhiya.oss-cn-shenzhen.aliyuncs.com/image/avatar4.jpg",
      "https://qiheizhiya.oss-cn-shenzhen.aliyuncs.com/image/avatar8.jpg",
      "https://qiheizhiya.oss-cn-shenzhen.aliyuncs.com/image/avatar2.jpg",
    ],
  },
});

Mock.mock(/^\/api\/comment\/?(\?.+)?$/, "get", function (options) {
  const query = qs.parse(options.url);

  return Mock.mock({
    code: 0,
    msg: "",
    data: {
      "total|50-200": 0,
      [`rows|${query.limit || 10}`]: [
        {
          id: "@guid",
          nickname: "@cname",
          content: "@cparagraph(1, 10)",
          createDate: Date.now(),
          "avatar|1": [
            "http://localhost:4000/uploads/image/vscode.png",
            "http://localhost:4000/uploads/image/g36.jpg",
            "http://localhost:4000/uploads/image/ai/g36.png",
          ],
        },
      ],
    },
  });
});